use axum::{
    http::{
        header::{AsHeaderName, USER_AGENT},
        HeaderMap,
    },
    response::IntoResponse,
    routing::get,
    Router,
};
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    let listener = TcpListener::bind("127.0.0.1:9527").await.unwrap();
    let app = Router::new().route("/", get(info));
    axum::serve(listener, app).await.unwrap();
}

async fn info(headers: HeaderMap) -> impl IntoResponse {
    let mut output = vec![];

    for k in [
        USER_AGENT,
        "X-REAL-IP".parse().unwrap(),
        "X-FORWARDED-FOR".parse().unwrap(),
        "CF-CONNECTING-IP".parse().unwrap(),
    ]
    .iter()
    {
        let v = get_header(&headers, k);
        output.push(format!("{}: {}", k.to_string(), v));
    }

    let ip = get_ip(&headers);

    output.push(format!("--- IP ----: {}", &ip));

    for (k, v) in headers.iter() {
        output.push(format!("{}: {:?}", k.to_string(), v));
    }

    output.join("\n")
}

fn get_header(headers: &HeaderMap, key: impl AsHeaderName) -> &str {
    headers.get(key).and_then(|v| v.to_str().ok()).unwrap_or("")
}

fn get_ip(headers: &HeaderMap) -> &str {
    let cf_connection_ip = get_header(&headers, "CF-CONNECTING-IP");
    let forwarded_for = get_header(&headers, "X-FORWARDED-FOR");
    let real_ip = get_header(&headers, "X-REAL-IP");

    if !cf_connection_ip.is_empty() {
        return cf_connection_ip;
    }
    if !forwarded_for.is_empty() {
        let forwarded_for_arr = forwarded_for.split(",").collect::<Vec<_>>();
        return forwarded_for_arr.get(0).copied().unwrap_or(real_ip);
    }
    real_ip
}
